package com.sikaryofficial.qrcode.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sikaryofficial.common.core.exception.ServiceException;
import com.sikaryofficial.common.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author : qinjinyuan
 * @desc : 产品工具类
 * @date : 2023/11/30 11:03
 */
@Slf4j
public class ProductQrUtil {

    public static final String PARAM_SPLIT_KEY = "-";
    private static final Map<Integer, String> monthMap = new HashMap<>();
    private static final String ENCODING_CHARS = "abcdefghijklmnopqrstuvwxyz";
    private static final int BASE_YEAR = 2024;

    static {
        monthMap.put(1, "a");
        monthMap.put(2, "b");
        monthMap.put(3, "c");
        monthMap.put(4, "d");
        monthMap.put(5, "e");
        monthMap.put(6, "f");
        monthMap.put(7, "g");
        monthMap.put(8, "h");
        monthMap.put(9, "i");
        monthMap.put(10, "j");
        monthMap.put(11, "k");
        monthMap.put(12, "l");
    }

    /**
     * 构建扫码参数
     * <p>
     * productShortId + 16进制的seqNo + 随机数 + 年月日16进制字符串
     *
     * @param productShortId 产品ID
     * @param seqNo          long的16进制字符串
     * @param randomLen      随机数长度
     * @param yearMonthStr   年月日16进制字符串
     * @return
     */
    public static String buildProductScanParam(Integer productShortId, Long seqNo, Integer randomLen, String yearMonthStr) {
        return productShortId.toString().concat(PARAM_SPLIT_KEY)
                .concat(Long.toHexString(seqNo)).concat(PARAM_SPLIT_KEY)
                .concat(StringUtils.generateRandomString(randomLen)).concat(PARAM_SPLIT_KEY)
                .concat(yearMonthStr);
    }

    /**
     * TODO : 这个是测试方法,待删除
     *
     * @param seqNo
     * @param randomLen
     * @return
     */
    public static String buildProductScanParam(Long seqNo, Integer randomLen) {
        return ""
                .concat(Long.toHexString(seqNo))
                .concat(StringUtils.generateRandomString(randomLen));
    }

    public static void main(String[] args) {
//        String baseUrl = "https://m.hismk.club";
//        // 循环生成2万个二维码编码
//        for (long i = 0; i < 20000L; i++) {
//           String qrCode = buildProductScanParam(i,7);
//           if(qrCode.length() > 8){
//               qrCode= qrCode.substring(0,8);
//           }
//            // 输出或进一步处理
//            System.out.println(baseUrl + "?p=" + qrCode + "&e=1" + " "+ qrCode);
//        }
        System.out.println(decodeYearMonth("bafl31"));
        System.out.println(encodeDateToStr(2705, 12, 31));
    }

    public static boolean isAlphanumeric(String input) {
        String regex = "^[a-z0-9]+$";
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(input);
        return matcher.matches();
    }

    public static Long getIdByHexStr(String hexValue) {
        // 16进制，radix 参数请勿调整
        return Long.parseUnsignedLong(hexValue, 16);
    }

    /**
     * 编码日期  ：注 最多支持到 2699
     * 使用多字符编码，支持更多年份
     */
    public static String encodeDateToStr(Integer year, Integer month, Integer day) {
        String encodedYear = encodeYear(year);
        String encodedMonth = monthMap.get(month);
        return encodedYear.concat(encodedMonth).concat(day.toString());
    }

    /**
     * 将年份转换为多字符编码
     */
    private static String encodeYear(int year) {
        StringBuilder sb = new StringBuilder();
        int offset = year - BASE_YEAR;
        do {
            sb.insert(0, ENCODING_CHARS.charAt(offset % ENCODING_CHARS.length()));
            offset /= ENCODING_CHARS.length();
        } while (offset > 0);
        return sb.toString();
    }

    /**
     * 支持单字符和多字符解码
     */
    public static Integer decodeYearMonth(String encodeDate) {
        String lettersOnly = extractLettersOnly(encodeDate);
        if (lettersOnly.length() < 2) {
            throw new ServiceException("Encoded date must contain at least one year and one month letter");
        }

        String yearStr = lettersOnly.substring(0, lettersOnly.length() - 1);
        String monthStr = lettersOnly.substring(lettersOnly.length() - 1);

        int year = decodeYear(yearStr);
        int month = decodeMonth(monthStr);
        int day = extractDay(encodeDate);

        return year * 10000 + month * 100 + day;
    }

    private static String extractLettersOnly(String encodeDate) {
        StringBuilder sb = new StringBuilder();
        for (char c : encodeDate.toCharArray()) {
            if (Character.isLetter(c)) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    private static int extractDay(String encodedDate) {
        int i = encodedDate.length() - 1;
        StringBuilder dayBuilder = new StringBuilder();

        // 从右往左遍历，提取连续数字
        while (i >= 0 && Character.isDigit(encodedDate.charAt(i))) {
            dayBuilder.insert(0, encodedDate.charAt(i));
            i--;
        }

        String dayStr = dayBuilder.toString();
        if (dayStr.isEmpty()) {
            throw new ServiceException("No numeric day found in encoded date");
        }

        int day = Integer.parseInt(dayStr);
        if (day < 1 || day > 31) {
            throw new ServiceException("Invalid day value: " + day);
        }

        return day;
    }

    private static int decodeYear(String encodedYear) {
        int value = 0;
        for (char c : encodedYear.toCharArray()) {
            value = value * ENCODING_CHARS.length() + ENCODING_CHARS.indexOf(c);
        }
        return BASE_YEAR + value;
    }

    private static int decodeMonth(String encodedMonth) {
        return monthMap.entrySet().stream()
                .filter(e -> CharSequenceUtil.equals(e.getValue(), encodedMonth))
                .map(Map.Entry::getKey)
                .findFirst()
                .orElseThrow(() -> new ServiceException("Invalid month code"));
    }

    /**
     * 把文件打包并输出到输出流 response.getOutputStream()
     * <p>
     *
     * @param folder
     * @return 返回 zip文件绝对路径
     */
    public static String genLocalZipFile(File folder, Long objectId, Long taskId) {
        String outputZipPath = "/data/tmp/qrcode/output/{0}/{1}/{2}/";
        outputZipPath = MessageFormat.format(outputZipPath, objectId.toString(), taskId.toString(),
                LocalDateTimeUtil.format(LocalDateTime.now(), DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        File dir = new File(outputZipPath);
        dir.mkdirs();
        String outputZipFile = outputZipPath.concat(IdWorker.getIdStr().concat(".zip"));

        try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(new File(outputZipFile).toPath()))) {
            // 获取图片文件夹下的所有文件
            File[] files = folder.listFiles();
            int count = 0;
            assert files != null;
            for (File file : files) {
                if (file.isFile()) {
                    // 添加文件到压缩文件
                    out.putNextEntry(new ZipEntry(file.getName()));
                    FileInputStream in = new FileInputStream(file);
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = in.read(buffer)) > 0) {
                        out.write(buffer, 0, length);
                    }
                    in.close();
                    out.closeEntry();
                    count++;
                }
            }
            log.info("downloadQrCode success, count:{}", count);
        } catch (Exception e) {
            log.error("productExport:{}", e);
            throw new ServiceException("downloadQrCode error.");
        } finally {
            // 删除图片文件夹下的所有文件
            FileUtil.del(folder);
        }
        return outputZipFile;
    }


    /**
     * 合并文件，并把合并文件压缩到zip文件中
     *
     * @param sourceFolder
     * @param objectId
     * @param taskId
     * @return
     */
    public static String genLocalZipFile1(File sourceFolder, Long objectId, Long taskId) {
        String outputZipPath = "/data/tmp/qrcode/output/{0}/{1}/{2}/";
        outputZipPath = MessageFormat.format(outputZipPath, objectId.toString(), taskId.toString(),
                LocalDateTimeUtil.format(LocalDateTime.now(), DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN)));
        String targetMergeFile = outputZipPath.concat("merge");
        File dir = new File(outputZipPath);
        dir.mkdirs();
        // 获取图片文件夹下的所有文件
        File mergeFileDir = new File(outputZipPath.concat("merge"));
        mergeFileDir.mkdirs();
        String outputZipFile = outputZipPath.concat(IdWorker.getIdStr().concat(".zip"));
        try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(new File(outputZipFile).toPath()))) {
            // 获取sourceFolder 文件夹下的所有文件
            List<Path> paths = Files.walk(Paths.get(sourceFolder.getPath()))
                    .filter(Files::isRegularFile)
                    .collect(Collectors.toList());
            // 合并当前文件夹下的所有文件

            File mergeFile1 = new File(targetMergeFile.concat(File.separator).concat("all_merge.xlsx"));
            mergeExcelFiles(mergeFile1, paths);
            if (mergeFile1.isFile()) {
                // 添加文件到压缩文件
                out.putNextEntry(new ZipEntry(mergeFile1.getName()));
                FileInputStream in = new FileInputStream(mergeFile1);
                byte[] buffer = new byte[1024];
                int length;
                while ((length = in.read(buffer)) > 0) {
                    out.write(buffer, 0, length);
                    out.flush();
                }
                in.close();
                out.closeEntry();
            }
            log.info("genLocalZipFile1 success, mergeFileDir:{}", mergeFile1.getName());
        } catch (Exception e) {
            log.error("genLocalZipFile1:{}", e);
            throw new ServiceException("genLocalZipFile1 error.");
        } finally {
            FileUtil.del(sourceFolder);
        }
        return outputZipFile;
    }


    public static void mergeExcelFiles(File targetMergeFile, List<Path> sourceFilePaths) throws IOException {
        // 创建一个新的Excel工作簿作为目标文件
        try (Workbook targetWorkbook = new XSSFWorkbook();
             FileOutputStream fos = new FileOutputStream(targetMergeFile)) {
            // 遍历源文件路径列表
            int sourceFileSize = sourceFilePaths.size();
            for (int i = 0; i <= sourceFileSize; i++) {
                Path sourcePath = sourceFilePaths.get(i);
                FileInputStream fis = new FileInputStream(sourcePath.getParent().toString().concat(File.separator)
                        .concat(sourcePath.getFileName().toString()));
                Workbook sourceWorkbook = WorkbookFactory.create(fis); // 打开源文件
                // 只读取第0个sheet
                Sheet sourceSheet = sourceWorkbook.getSheetAt(0);
                Sheet targetSheet = targetWorkbook.createSheet("ske_product" + i);
                // 设置cell的宽度
                // 设置某一列（例如第一列A列）的宽度，单位为字符数，默认宽度为256个EMU（Excel的最小单位）
                targetSheet.setColumnWidth(0, 50 * 256);
                targetSheet.setColumnWidth(1, 20 * 256);
                // 将源sheet的数据复制到新sheet中
                copySheetData(sourceSheet, targetSheet, targetWorkbook);
                sourceWorkbook.close();
                fis.close();
            }
            targetWorkbook.write(fos);
        } catch (Exception e) {
            log.error("mergeExcelFiles error:{}", e);
        }
    }

    private static void copySheetData(Sheet sourceSheet, Sheet targetSheet, Workbook targetWorkbook) {
        int lastRowNum = sourceSheet.getLastRowNum();
        for (int i = 0; i < lastRowNum; i++) {
            Row targetRow = targetSheet.createRow(i);
            Row sourceRow = sourceSheet.getRow(i);
            int lasCellNum = sourceRow.getLastCellNum();
            // Cell sourceCell : sourceRow
            for (int j = 0; j < lasCellNum; j++) {
                Cell targetCell = targetRow.createCell(j);
                // 样式如何拷贝
                if (i == 0) {
                    copyCellStyle(sourceRow.getCell(j), targetCell, targetWorkbook);
                }
                copyCellContent(sourceRow.getCell(j), targetCell);
            }
        }
    }

    private static void copyCellStyle(Cell sourceCell, Cell targetCell, Workbook targetWorkbook) {
        long start = System.currentTimeMillis();
        CellStyle sourceCellStyle = sourceCell.getCellStyle();
        // 克隆样式以避免样式共享问题
        CellStyle clonedStyle = targetWorkbook.createCellStyle();
        clonedStyle.cloneStyleFrom(sourceCellStyle);
        // 将克隆的样式应用到目标单元格
        targetCell.setCellStyle(clonedStyle);
        log.info("cost:{}ms", System.currentTimeMillis() - start);
    }

    private static void copyCellContent(Cell sourceCell, Cell targetCell) {
        switch (sourceCell.getCellType()) {
            case STRING:
                targetCell.setCellValue(sourceCell.getStringCellValue());
                break;
            case NUMERIC:
                targetCell.setCellValue(sourceCell.getNumericCellValue());
                break;
            // 类似的处理逻辑，包括 BOOLEAN、FORMULA、ERROR 等类型
            // ...
        }
    }
}
